package com.ncs.nc.controller;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.ncs.nc.entity.Constant;
import com.ncs.nc.entity.GoodsType;
import com.ncs.nc.entity.ResultObject;
import com.ncs.nc.service.impl.RedisServiceImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

@RequestMapping("/redis")
@RestController
public class RedisController {

    private static final Logger log= LoggerFactory.getLogger(RedisController.class);

    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private ObjectMapper objectMapper;
    @Autowired
    private ResultObject ro;

    /**
     * 测试Redis字符串缓存类型
     */
    @RequestMapping("/redisStringTest")
    public void testStringRedis(){
        System.out.println("--开始RedisTemplate操作组件实战--");

        final String key="redisTest";
        final String value="Have a try";

        //Redis通用操作组件
        ValueOperations valueOperations=redisTemplate.opsForValue();
        System.out.println("将Key:{}写入缓存中，其对应的取值Value:{}"+key+"--"+value);
        valueOperations.set(key,value);

        Object result=valueOperations.get(key);
        System.out.println("将Key:{}读出来，其对应的取值Value:{}"+result);
    }

    /**
     * 测试redis对象缓存类型
     * @throws Exception
     */
    @RequestMapping("/redisObjectTest")
    public void testObjectRedis() throws Exception {
        System.out.println("--对象操作--");

        GoodsType gt=new GoodsType(1,"测试");
        //Redis通用操作组件
        ValueOperations valueOperations=redisTemplate.opsForValue();

        final String key="redisObjTest";
        final String value=objectMapper.writeValueAsString(gt);

        //写入
        System.out.println("将Key:{}写入缓存中，其对应的取值Value:{}"+key+"--"+value);
        valueOperations.set(key,value);
        System.out.println(1);
        //读出
        Object result=valueOperations.get(key);
        if (result!=null){
            GoodsType gtRead=objectMapper.readValue(result.toString(),GoodsType.class);
            System.out.println("将Key:{}读出来，其对应的取值Value:{}"+gtRead.getGoodsTypeName());
        }
    }


    /**
     * 测试Redis的键是否还存在
     * @throws Exception
     */
    @RequestMapping("/testKeyExist")
    public void testKeyExist() throws Exception{

        //构造Key与Redis操作组件ValueOperations
        final String keyA="keyA";
        ValueOperations valueOperations=redisTemplate.opsForValue();

        //1. 赋值时，添加ttl，时间一到，自动清理失效，ttl单位为秒
        valueOperations.set(keyA,"expire操作",10L, TimeUnit.SECONDS);
        //2. 等待5秒，判断Key是否存在
        Thread.sleep(5000);
        Boolean isExist=redisTemplate.hasKey(keyA);
        Object value=valueOperations.get(keyA);
        System.out.println(isExist);
        System.out.println(value);

        //再等5秒执行相同操作
        Thread.sleep(5000);
        isExist=redisTemplate.hasKey(keyA);
        value=valueOperations.get(keyA);
        System.out.println(isExist);
        System.out.println(value);

    }

//    @Scheduled(cron = "0/1 * * * * ?")
//    public void test(){
//        System.out.println(1);
//    }


    /**
     * 缓存击穿模拟
     */
    @Autowired
    private RedisServiceImpl redisService;

    /**
     * 正常情况下的缓存查询过程
     */
    @GetMapping("/data/v1")
    public ResultObject getData(){
        ro.setMsg("success");
        try {
            //直接调用redisService类的方法即可
            ro.setData(redisService.getByIdV1(Constant.RedisJiChuanId));
        }catch (Exception ex){
            ex.printStackTrace();
            ro.setMsg("failed");
        }
        return ro;
    }

    /**
     * 创建拥有20个固定线程的线程池
     */
    private static final ExecutorService EXECUTOR_SERVICE= Executors.newFixedThreadPool(20);

    /**
     * 并发情况下缓存击穿复现--自模拟发起多线程并发访问
     * 注释部分的代码为分布式锁解决方案，去掉这行注释，查看结果的不同
     */
    @GetMapping("/data/v2")
    public void getDataV2(){
        for (int i = 0; i < 10000; i++) {
            EXECUTOR_SERVICE.submit(new Runnable() {
                @Override
                public void run() {
                    //发起对接口的访问
                    redisService.getByIdV1(Constant.RedisJiChuanId);
//                    //分布式锁的控制
//                    redisService.getByIdV2(Constant.RedisJiChuangId);
                }
            });
        }
    }

    /**
     * 并发情况下缓存穿透复现--自模拟发起多线程并发访问
     */
    @GetMapping("/data/v3")
    public void getDataV3(){
        for (int i = 0; i < 10000; i++) {
            EXECUTOR_SERVICE.submit(new Runnable() {
                @Override
                public void run() {
                    //发起对接口的访问
                    redisService.getByIdV3(Constant.RedisChuanTouId);
                }
            });
        }
    }
}
